﻿using System;
using System.ComponentModel;
using System.Linq.Expressions;
using System.Runtime.InteropServices;
using System.Windows;
using System.Windows.Interop;
using MyFramework.Common;
using MyFramework.Common.Enums;
using MyFramework.Common.Interfaces;

namespace MyFramework.Core
{
    /// <summary>
    /// View
    /// </summary>
    public abstract class View : Window, IView
    {
        #region Fields

        #region INotifyPropertyChanging Members

        /// <summary>
        /// Occurs when a property value is changing.
        /// </summary>
        public event PropertyChangingEventHandler PropertyChanging;

        #endregion

        #region INotifyPropertyChanged Members

        /// <summary>
        /// Occurs when a property value changes.
        /// </summary>
        public event PropertyChangedEventHandler PropertyChanged;

        #endregion

        /// <summary>
        /// The state
        /// </summary>
        private ViewState state;

        /// <summary>
        /// The gwl style
        /// </summary>
        private const int GWL_STYLE = -16;

        /// <summary>
        /// The forwards sys menu
        /// </summary>
        private const int WS_SYS_MENU = 0x80000;

        /// <summary>
        /// Gets the window long.
        /// </summary>
        /// <param name="hWnd">The authentication WND.</param>
        /// <param name="nIndex">Index of the asynchronous.</param>
        /// <returns></returns>
        [DllImport("user32.dll", SetLastError = true)]
        private static extern int GetWindowLong(IntPtr hWnd, int nIndex);

        /// <summary>
        /// Sets the window long.
        /// </summary>
        /// <param name="hWnd">The authentication WND.</param>
        /// <param name="nIndex">Index of the asynchronous.</param>
        /// <param name="dwNewLong">The dw new long.</param>
        /// <returns></returns>
        [DllImport("user32.dll")]
        private static extern int SetWindowLong(IntPtr hWnd, int nIndex, int dwNewLong);

        #endregion

        /// <summary>
        /// Initializes a new instance of the <see cref="View"/> class.
        /// </summary>
        public View()
        {
            this.Loaded += new RoutedEventHandler(View_Loaded);
        }

        #region Properties

        /// <summary>
        /// Gets or sets the data context for an element when it participates in data binding.
        /// </summary>
        /// <returns>The object to use as data context.</returns>
        public new IViewModel DataContext
        {
            get
            {
                return (IViewModel)base.DataContext;
            }
            private set
            {
                base.DataContext = value;
            }
        }

        /// <summary>
        /// Gets or sets the state.
        /// </summary>
        /// <value>
        /// The state.
        /// </value>
        /// <exception cref="System.NotImplementedException">
        /// </exception>
        public ViewState State
        {
            get
            {
                return state;
            }
            set
            {
                this.state = value;
                this.RaisePropertyChangedEvent(() => this.State);
            }
        }

        #endregion

        #region Methods

        /// <summary>
        /// Initializes the specified viewmodel.
        /// </summary>
        /// <param name="viewmodel">The viewmodel.</param>
        public void Initialize(IViewModel viewmodel)
        {
            this.DataContext = viewmodel;
        }

        /// <summary>
        /// Handles the Loaded event of the View control.
        /// </summary>
        /// <param name="sender">The source of the event.</param>
        /// <param name="e">The <see cref="EventArgs"/> instance containing the event data.</param>
        public void View_Loaded(object sender, EventArgs e)
        {
            var hwnd = new WindowInteropHelper(this).Handle;
            SetWindowLong(hwnd, GWL_STYLE, GetWindowLong(hwnd, GWL_STYLE) & ~WS_SYS_MENU);
        }

        #region IEntity Members

        /// <summary>
        /// Raises the property changing event.
        /// </summary>
        /// <typeparam name="TProperty">The type of the property.</typeparam>
        /// <param name="projection">The projection.</param>
        public void RaisePropertyChangingEvent<TProperty>(Expression<Func<TProperty>> projection)
        {
            RaisePropertyChangingEvent(projection.GetPropertyName());
        }

        /// <summary>
        /// Raises the property changed event.
        /// </summary>
        /// <typeparam name="TProperty">The type of the property.</typeparam>
        /// <param name="projection">The projection.</param>
        public void RaisePropertyChangedEvent<TProperty>(Expression<Func<TProperty>> projection)
        {
            RaisePropertyChangedEvent(projection.GetPropertyName());
        }

        /// <summary>
        /// Raises the property changed event.
        /// </summary>
        /// <param name="propertyName">Name of the property.</param>
        public void RaisePropertyChangedEvent(string propertyName)
        {
            if (PropertyChanged != null)
            {
                this.PropertyChanged.RaiseChanged(this, propertyName);
            }
        }

        /// <summary>
        /// Raises the property changing event.
        /// </summary>
        /// <param name="propertyName">Name of the property.</param>
        public void RaisePropertyChangingEvent(string propertyName)
        {
            if (PropertyChanging != null)
            {
                this.PropertyChanging.RaiseChanging(this, propertyName);
            }
        }

        #endregion

        #region IDisposable Members

        /// <summary>
        /// Performs application-defined tasks associated with freeing, releasing, or resetting unmanaged resources.
        /// </summary>
        public void Dispose()
        {
            if (this.State < ViewState.Disposing)
            {
                try
                {
                    this.State = ViewState.Disposing;

                    if (this.DataContext != null)
                    {
                        this.DataContext.Views.Clear();
                    }
                    this.Dispose(true);
                }
                finally
                {
                    this.State = ViewState.Disposed;
                }
            }
            GC.SuppressFinalize(this);
        }

        /// <summary>
        /// Releases unmanaged and - optionally - managed resources.
        /// </summary>
        /// <param name="p"><c>true</c> to release both managed and unmanaged resources; <c>false</c> to release only unmanaged resources.</param>
        protected virtual void Dispose(bool p)
        {

        }

        #endregion

        #endregion
    }
}
